package cn.yzw.jc.sandbox.spring.monitor;

import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.text.csv.CsvWriter;
import cn.hutool.core.util.CharsetUtil;
import cn.yzw.jc.sandbox.spring.domain.SpringBeanModel;
import cn.yzw.jc.sandbox.spring.listener.SpringBeanInitListener;
import com.alibaba.jvm.sandbox.api.event.Event;
import com.alibaba.jvm.sandbox.api.filter.Filter;
import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author liucan@yzw.cn write on 2024/1/8 15:11
 */
public class SpringBeanStartMonitor extends Thread {
    private final static Logger log = LoggerFactory.getLogger(SpringBeanStartMonitor.class);

    private final ModuleEventWatcher moduleEventWatcher;
    private final SpringBeanInitListener springBeanInitListener;

    public SpringBeanStartMonitor(ModuleEventWatcher moduleEventWatcher) {
        this.moduleEventWatcher = moduleEventWatcher;
        this.springBeanInitListener = new SpringBeanInitListener();
    }

    @SneakyThrows
    @Override
    public void run() {

        // 开启SpringBean初始化监听
        Filter springBeanFilter = new Filter() {
            @Override
            public boolean doClassFilter(int access, String javaClassName, String superClassTypeJavaClassName, String[] interfaceTypeJavaClassNameArray,
                                         String[] annotationTypeJavaClassNameArray) {
                // 匹配springBean初始化切面类
                boolean springBean = "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory".equals(javaClassName);
                if (springBean) {
                    return true;
                }
                // 匹配springboot application启动切面类
                return "org.springframework.boot.SpringApplication".equals(javaClassName);
            }

            @Override
            public boolean doMethodFilter(int access, String javaMethodName, String[] parameterTypeJavaClassNameArray, String[] throwsTypeJavaClassNameArray,
                                          String[] annotationTypeJavaClassNameArray) {
                // 匹配springBean初始化切面方法
                boolean springBean = "initializeBean".equalsIgnoreCase(javaMethodName);
                if (springBean) {
                    return true;
                }

                // 匹配springboot application启动切面方法
                if (!"run".equalsIgnoreCase(javaMethodName)) {
                    return false;
                }

                // 如果是 run 方法，检查参数列表
                if (parameterTypeJavaClassNameArray != null && parameterTypeJavaClassNameArray.length == 1) {
                    // 检查参数是否为字符串数组
                    return "java.lang.String[]".equals(parameterTypeJavaClassNameArray[0]);
                }
                return false;
            }
        };
        int springBeanWatchId = moduleEventWatcher.watch(springBeanFilter, springBeanInitListener, Event.Type.BEFORE, Event.Type.RETURN);

        // 检查应用状态并结束监听
        while (true) {
            Thread.sleep(500L);
            if (springBeanInitListener.getApplicationStartStatus()) {
                moduleEventWatcher.delete(springBeanWatchId);
                break;
            }

        }
        printSpringBeanCosts();
    }


    /**
     * 导出数据
     */
    public void printSpringBeanCosts() {
        Map<String, SpringBeanModel> springBeanMap = springBeanInitListener.getSpringBeanMap();
        List<SpringBeanModel> springBeanModelList = springBeanMap.values().stream().filter(o -> o.getCost() > 0).sorted(Comparator.comparing(SpringBeanModel::getCost).reversed()).collect(Collectors.toList());
        String filePath = System.getProperty("user.dir") + "/var/logs/springBean.csv";
        CsvWriter writer = CsvUtil.getWriter(filePath, CharsetUtil.CHARSET_UTF_8);
        writer.writeBeans(springBeanModelList);
        writer.close();
        log.info("Spring bean 启动耗时统计结束，输出文件路径:{}", filePath);
    }
}
